package com.xcity.game.combat;

import static com.xcity.game.combat.attr.CombatAttributeType.AVOID_CRIT;
import static com.xcity.game.combat.attr.CombatAttributeType.CRIT;
import static com.xcity.game.combat.attr.CombatAttributeType.DODGE;
import static com.xcity.game.combat.attr.CombatAttributeType.HIT_RATE;
import static com.xcity.game.combat.attr.CombatAttributeType.MAGIC_HIT_RATE;

import java.util.List;

import com.xcity.game.combat.attr.CombatAttributeType;
import com.xcity.game.combat.template.ClazzRestrainTemplate;
import com.xcity.game.combat.template.CombatAttributeTemplate;
import com.xcity.game.combat.template.CombatPowerConfig;
import com.xcity.game.combat.unit.CombatUnit;
import com.xcity.game.combat.unit.CombatableBase;
import com.xcity.game.employee.Clazz;
import com.xcity.game.employee.EmployeeUtils;
import com.xcity.game.npc.NpcCombatable;
import com.xcity.game.npc.NpcTeamTemplate;
import com.xcity.game.npc.NpcTemplate;

import naga.x.App;
import naga.x.game.template.TemplateService;
import naga.x.game.template.annotation.Excel;

public class CombatUtils {
	
	private static CombatAttributeTemplate playerTemplate;
	private static List<ClazzRestrainTemplate> clazzRestrains; // 职业克制配置
	
	@Excel(value = "combat.xlsx", sheet = 3, isStatic = true)
	public static class Config {
		/** 攻击浮动 */
		public static float attackMinRatio = 0.95f, attackFloatRange = 0.1f;
		/** 破防系数 */
		public static float defenseBrokenRatio = 0.15f;
		/** 回合上限 */
		public static int roundsMax = 15;
		private Config() {}
	}
	
	public static int calcCombatPower(NpcTeamTemplate npcTeam) {
		int val = 0;
		NpcTemplate boss = npcTeam.getBoss();
		if (boss != null) {
			val += calcCombatPower((NpcCombatable) boss.toCombatabale());
		}
		NpcTemplate[] npcs = npcTeam.getNpcs();
		if (npcs != null) {
			for (NpcTemplate npc : npcs) {
				if (npc != null) {
					val += calcCombatPower((NpcCombatable) npc.toCombatabale());
				}
			}
		}
		return val;
	}

	public static int calcCombatPower(CombatableBase combatable) {
		float val = 0;
		CombatAttributeType[] types = CombatAttributeType.vals;
		for (CombatAttributeType type : types) {
			val += combatable.getAttributeValue(type) * CombatPowerConfig.getRatio(type);
		}
		return (int) val;
	}
	
	/**
	 * 获取所有战斗单位战斗力总和<br/>
	 * 不需要重新计算，直接累加
	 * @param leader
	 * @param combatables
	 * @return
	 */
	public static int getCombatPower(CombatableBase leader, CombatableBase[] combatables) {
		int val = 0;
		if (leader != null) {
			val += leader.getCombatPower();
		}
		if (combatables != null && combatables.length > 0) {
			for (CombatableBase c : combatables) {
				if (c != null) {
					val += c.getCombatPower();
				}
			}
		}
		return val;
	}
	
	public static CombatAttributeTemplate getDefaultPlayerTemplate() {
		return playerTemplate != null ? playerTemplate : (playerTemplate = App.getApp().getServiceManager().get(TemplateService.class).get(CombatAttributeTemplate.class, EmployeeUtils.DEFAULT_PLAYER_TEMPLATE_ID));
	}
	
	/**
	 * 计算一次普攻
	 * @param effect
	 */
	public static void calcAttack(ActionEffect effect) {
		// 1. 计算是否命中
		CombatUnit source = effect.ref.unit;
		CombatUnit target = effect.unit;
		float n = target.getDodge() * DODGE.scale  - source.getHitRate() * HIT_RATE.scale;
		effect.result = ActionEffect.RESULT_HIT;
		if (n > 0) {
			float odds = Combat.RND.nextFloat();
			if (odds < n) { // miss
				effect.result = ActionEffect.RESULT_MISS;
			}
		}
		if (effect.isHit()) {
			// 2. 若命中，计算暴击
			n = source.getCrit() * CRIT.scale - target.getAvoidCrit() * AVOID_CRIT.scale;
				// 攻击浮动范围：0.95-1.05
			float power = source.getAttackPower() * (Combat.RND.nextFloat() * Config.attackFloatRange + Config.attackMinRatio);
				// 理论伤害 (攻-防)x职业克制系数
			float clazzFactor = getClazzRestrainFactor(source.getClazz(), target.getClazz());
			int damage1 = (int) ((power - target.getDefense()) * clazzFactor);
				// 破防伤害 攻x破防系数x职业克制系数
			int damage2 = (int) (power * Config.defenseBrokenRatio * clazzFactor);
				// 最终伤害取理论伤害、破防伤害中最大值
			int damage = Math.max(damage1, damage2);
			if (n > 0f && Combat.RND.nextFloat() < n) { // 产生暴击
				effect.result = ActionEffect.RESULT_CRIT;
				damage += damage;
			}
			// 3. 计算伤害
			effect.effect(CombatAttributeType.HP, -damage);
		}
	}
	
	/**
	 * 计算一次法攻
	 * @param effect
	 */
	public static void calcMagicAttack(ActionEffect effect) {
		calcMagicAttack(effect, 1f);
	}
	
	public static void calcMagicAttack(ActionEffect effect, float magicPowerRate) {
		// 1. 计算是否命中
		CombatUnit source = effect.ref.unit;
		CombatUnit target = effect.unit;
		float n = target.getDodge() * DODGE.scale  - source.getMagicHitRate() * MAGIC_HIT_RATE.scale;
		effect.result = ActionEffect.RESULT_HIT;
		if (n > 0) {
			float odds = Combat.RND.nextFloat();
			if (odds < n) { // miss
				effect.result = ActionEffect.RESULT_MISS;
			}
		}
		if (effect.isHit()) {
			// 2. 若命中，计算暴击
			n = source.getCrit() * CRIT.scale - target.getAvoidCrit() * AVOID_CRIT.scale;
				// 攻击浮动范围：0.95-1.05
			float power = magicPowerRate * source.getMagicPower() * (Combat.RND.nextFloat() * Config.attackFloatRange + Config.attackMinRatio);
				// 理论伤害 (攻-防)x职业克制系数
			float clazzFactor = getClazzRestrainFactor(source.getClazz(), target.getClazz());
			int damage1 = (int) ((power - target.getMagicDefense()) * clazzFactor);
				// 破防伤害 攻x破防系数x职业克制系数
			int damage2 = (int) (power * Config.defenseBrokenRatio * clazzFactor);
				// 最终伤害取理论伤害、破防伤害中最大值
			int damage = Math.max(damage1, damage2);
			if (n > 0f && Combat.RND.nextFloat() < n) { // 产生暴击
				effect.result = ActionEffect.RESULT_CRIT;
				damage += damage;
			}
			// 3. 计算伤害
			effect.effect(CombatAttributeType.HP, -damage);
		}
	}
	
	private static float getClazzRestrainFactor(Clazz source, Clazz target) {
		if (source == null || source == Clazz.UNKNOWN) {
			source = Clazz.WARRIOR;
		}
		if (target == null || target == Clazz.UNKNOWN) {
			target = Clazz.WARRIOR;
		}
		if (clazzRestrains == null) {
			clazzRestrains = App.getApp().getServiceManager().get(TemplateService.class).getTemplatesList(ClazzRestrainTemplate.class);
		}
		List<ClazzRestrainTemplate> list = clazzRestrains;
		for (int i = 0; i < list.size(); i++) {
			ClazzRestrainTemplate crt = list.get(i);
			if (crt.getClazz() == source) {
				return crt.getFactor(target);
			}
		}
		return 1f;
	}
	
	public static int getAliveUnitCount(Combat combat) {
		return getAliveUnitCount(combat.getSource().all()) + getAliveUnitCount(combat.getTarget().all());
	}
	
	private static int getAliveUnitCount(List<CombatUnit> units) {
		int n = 0;
		for (int i = units.size() - 1; i >= 0; i--) {
			CombatUnit u = units.get(i);
			if (u != null && !u.isDied()) {
				n++;
			}
		}
		return n;
	}
	
}
